﻿using System;
using Kaleida.ServiceMonitor.Framework.Responses;
using Kaleida.ServiceMonitor.Operations.ResponseHandlers;
using NUnit.Framework;

namespace Kaleida.UnitTests.Operations
{
    [TestFixture]
    public class MustEqualTests
    {
        [Test]
        public void TestStringArgument()
        {
            Assert.AreEqual("'123'", new MustEqual("123").ProcessResponse("123"));
            Assert.AreEqual("'-123'", new MustEqual("-123").ProcessResponse("-123"));
            Assert.AreEqual("'abc'", new MustEqual("abc").ProcessResponse("abc"));
            
            var exception = Assert.Throws<InvalidOperationException>(() => new MustEqual("abc").ProcessResponse("Abc"));
            Assert.AreEqual("Expected response to equal 'abc' but was 'Abc'", exception.Message);
        }

        [Test]
        public void TestStringContainerArgument()
        {
            Assert.AreEqual("'123'", new MustEqual("123").ProcessResponse(new MockStringContainer("123")), "Content is used in preference to ToString()");
            Assert.AreEqual("'-123'", new MustEqual("-123").ProcessResponse(new MockStringContainer("-123")), "Content is used in preference to ToString()");
            Assert.AreEqual("'abc'", new MustEqual("abc").ProcessResponse(new MockStringContainer("abc")), "Content is used in preference to ToString()");

            var exception = Assert.Throws<InvalidOperationException>(() => new MustEqual("abc").ProcessResponse(new MockStringContainer("Abc")));
            Assert.AreEqual("Expected response to equal 'abc' but was 'Abc'", exception.Message);
        }

        [Test]
        public void TestIntegerArgument()
        {
            Assert.AreEqual("123", new MustEqual("123").ProcessResponse(123));
            Assert.AreEqual("-123", new MustEqual("-123").ProcessResponse(-123));
            
            var exception1 = Assert.Throws<InvalidOperationException>(() => new MustEqual("123.0").ProcessResponse(123));
            Assert.AreEqual("Expected response to equal '123.0' but was '123'", exception1.Message);

            var exception2 = Assert.Throws<InvalidOperationException>(() => new MustEqual("123").ProcessResponse(122));
            Assert.AreEqual("Expected response to equal 123 but was 122", exception2.Message);
        }

        [Test]
        public void TestDecimalArgument()
        {
            Assert.AreEqual("123.0", new MustEqual("123").ProcessResponse(123.0m));
            Assert.AreEqual("-123.0", new MustEqual("-123").ProcessResponse(-123.0m));
            Assert.AreEqual("123.0", new MustEqual("123.0").ProcessResponse(123.0m));
            Assert.AreEqual("123.456", new MustEqual("123.456").ProcessResponse(123.456m));
            
            var exception = Assert.Throws<InvalidOperationException>(() => new MustEqual("123.456").ProcessResponse(123.455m));
            Assert.AreEqual("Expected response to equal 123.456 but was 123.455", exception.Message);
        }

        [Test]
        public void TestTimeSpanArgument()
        {
            Assert.AreEqual("[00:03:00]", new MustEqual("3m").ProcessResponse(TimeSpan.FromMinutes(3)));
            Assert.AreEqual("[00:00:00.045]", new MustEqual("45ms").ProcessResponse(TimeSpan.FromMilliseconds(45)));
            Assert.AreEqual("[00:00:00.045]", new MustEqual("45").ProcessResponse(TimeSpan.FromMilliseconds(45)));
            
            var exception = Assert.Throws<InvalidOperationException>(() => new MustEqual("6m").ProcessResponse(TimeSpan.FromSeconds(6)));
            Assert.AreEqual("Expected response to equal [00:06:00] but was [00:00:06]", exception.Message);
        }

        [Test]
        public void TestTimedEventArgument()
        {
            Assert.AreEqual("Time Taken [00:03:00]", new MustEqual("3m").ProcessResponse(new TimedEvent {TimeTaken = TimeSpan.FromMinutes(3)}));
            Assert.AreEqual("Time Taken [00:00:00.045]", new MustEqual("45ms").ProcessResponse(new TimedEvent { TimeTaken = TimeSpan.FromMilliseconds(45) }));
            Assert.AreEqual("Time Taken [00:00:00.045]", new MustEqual("45").ProcessResponse(new TimedEvent { TimeTaken = TimeSpan.FromMilliseconds(45) }));
            
            var exception = Assert.Throws<InvalidOperationException>(() => new MustEqual("6m").ProcessResponse(new TimedEvent {TimeTaken = TimeSpan.FromSeconds(6)}));
            Assert.AreEqual("Expected response to equal [00:06:00] but was [00:00:06]", exception.Message);
        }

        private class TimedEvent : ITimedEvent
        {
            public TimeSpan TimeTaken { get; set; }
        }

        private class MockStringContainer : IStringContainer
        {
            private readonly string content;

            public MockStringContainer(string content)
            {
                this.content = content;
            }

            public string Content
            {
                get { return content; }
            }

            public override string ToString()
            {
                return "ToString() result";
            }
        }
    }
}
